/*
 * MIT License
 *
 * Copyright (C) 2023 Huawei Device Co., Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef URI_H
#define URI_H

#include <string>
#include <vector>
#include <folly/String.h>

namespace rnoh {
/**
 * Class representing a URI.
 *
 * Consider http://www.facebook.com/foo/bar?key=foo#anchor
 *
 * The URI is broken down into its parts: scheme ("http"), authority
 * (ie. host and port, in most cases: "www.facebook.com"), path
 * ("/foo/bar"), query ("key=foo") and fragment ("anchor").  The scheme is
 * lower-cased.
 *
 * If this Uri represents a URL, note that, to prevent ambiguity, the component
 * parts are NOT percent-decoded; you should do this yourself with
 * uriUnescape() (for the authority and path) and uriUnescape(...,
 * UriEscapeMode::QUERY) (for the query, but probably only after splitting at
 * '&' to identify the individual parameters).
 */
class Uri {
public:
    /**
     * Parse a Uri from a string.  Throws std::invalid_argument on parse error.
     */
    explicit Uri(folly::StringPiece str);

    const std::string& scheme() const
    {
        return scheme_;
    }
    const std::string& username() const
    {
        return username_;
    }
    const std::string& password() const
    {
        return password_;
    }
    /**
     * Get host part of URI. If host is an IPv6 address, square brackets will be
     * returned, for example: "[::1]".
     */
    const std::string& host() const
    {
        return host_;
    }
    /**
     * Get host part of URI. If host is an IPv6 address, square brackets will not
     * be returned, for example "::1"; otherwise it returns the same thing as
     * host().
     *
     * hostname() is what one needs to call if passing the host to any other tool
     * or API that connects to that host/port; e.g. getaddrinfo() only understands
     * IPv6 host without square brackets
     */
    std::string hostname() const;
    uint16_t port() const
    {
        return port_;
    }
    const std::string& path() const
    {
        return path_;
    }
    const std::string& query() const
    {
        return query_;
    }
    const std::string& fragment() const
    {
        return fragment_;
    }

    std::string authority() const;

    template <class String>
    String toString() const;

    std::string str() const
    {
        return toString<std::string>();
    }
    folly::fbstring fbstr() const
    {
        return toString<folly::fbstring>();
    }

    void setPort(uint16_t port)
    {
        hasAuthority_ = true;
        port_ = port;
    }

    /**
     * Get query parameters as key-value pairs.
     * e.g. for URI containing query string:  key1=foo&key2=&key3&=bar&=bar=
     * In returned list, there are 3 entries:
     *     "key1" => "foo"
     *     "key2" => ""
     *     "key3" => ""
     * Parts "=bar" and "=bar=" are ignored, as they are not valid query
     * parameters. "=bar" is missing parameter name, while "=bar=" has more than
     * one equal signs, we don't know which one is the delimiter for key and
     * value.
     *
     * Note, this method is not thread safe, it might update internal state, but
     * only the first call to this method update the state. After the first call
     * is finished, subsequent calls to this method are thread safe.
     *
     * @return  query parameter key-value pairs in a vector, each element is a
     *          pair of which the first element is parameter name and the second
     *          one is parameter value
     */
    const std::vector<std::pair<std::string, std::string>>& getQueryParams();
    bool isAlphaNumeric(char c);
    bool isReserved(char c);
    bool isAllowedEncode(char c, const std::string &allow);
    std::string uriEncode(const std::string &s, const std::string &allow);
    std::string& getEncodeUri()
    {
        return encodeUri_;
    };

private:
    std::string scheme_;
    std::string username_;
    std::string password_;
    std::string host_;
    bool hasAuthority_;
    uint16_t port_;
    std::string path_;
    std::string query_;
    std::string fragment_;
    std::vector<std::pair<std::string, std::string>> queryParams_;
    std::string encodeUri_;
};

} // namespace rnoh

#endif // URI_H
